home *** CD-ROM | disk | FTP | other *** search
/ Computer Shopper 240 (DVD) / Issue 240 - February 2008 - DPCS0208DVD.ISO / BO Expert tools / AutoHotKey / Source / AutoHotkey104702_source.exe / source / clipboard.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2007-06-24  |  22.4 KB  |  470 lines

  1. /*
  2. AutoHotkey
  3.  
  4. Copyright 2003-2007 Chris Mallett (support@autohotkey.com)
  5.  
  6. This program is free software; you can redistribute it and/or
  7. modify it under the terms of the GNU General Public License
  8. as published by the Free Software Foundation; either version 2
  9. of the License, or (at your option) any later version.
  10.  
  11. This program is distributed in the hope that it will be useful,
  12. but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14. GNU General Public License for more details.
  15. */
  16.  
  17. #include "stdafx.h" // pre-compiled headers
  18. #include "clipboard.h"
  19. #include "globaldata.h"  // for g_script.ScriptError() and g_ClipboardTimeout
  20. #include "application.h" // for MsgSleep()
  21. #include "util.h" // for strlcpy()
  22.  
  23. size_t Clipboard::Get(char *aBuf)
  24. // If aBuf is NULL, it returns the length of the text on the clipboard and leaves the
  25. // clipboard open.  Otherwise, it copies the clipboard text into aBuf and closes
  26. // the clipboard (UPDATE: But only if the clipboard is still open from a prior call
  27. // to determine the length -- see later comments for details).  In both cases, the
  28. // length of the clipboard text is returned (or the value CLIPBOARD_FAILURE if error).
  29. // If the clipboard is still open when the next MsgSleep() is called -- presumably
  30. // because the caller never followed up with a second call to this function, perhaps
  31. // due to having insufficient memory -- MsgSleep() will close it so that our
  32. // app doesn't keep the clipboard tied up.  Note: In all current cases, the caller
  33. // will use MsgBox to display an error, which in turn calls MsgSleep(), which will
  34. // immediately close the clipboard.
  35. {
  36.     // Seems best to always have done this even if we return early due to failure:
  37.     if (aBuf)
  38.         // It should be safe to do this even at its peak capacity, because caller
  39.         // would have then given us the last char in the buffer, which is already
  40.         // a zero terminator, so this would have no effect:
  41.         *aBuf = '\0';
  42.  
  43.     UINT i, file_count = 0;
  44.     BOOL clipboard_contains_text = IsClipboardFormatAvailable(CF_TEXT);
  45.     BOOL clipboard_contains_files = IsClipboardFormatAvailable(CF_HDROP);
  46.     if (!(clipboard_contains_text || clipboard_contains_files))
  47.         return 0;
  48.  
  49.     if (!mIsOpen)
  50.     {
  51.         // As a precaution, don't give the caller anything from the clipboard
  52.         // if the clipboard isn't already open from the caller's previous
  53.         // call to determine the size of what's on the clipboard (no other app
  54.         // can alter its size while we have it open).  The is to prevent a
  55.         // buffer overflow from happening in a scenario such as the following:
  56.         // Caller calls us and we return zero size, either because there's no
  57.         // CF_TEXT on the clipboard orthere was a problem opening the clipboard.
  58.         // In these two cases, the clipboard isn't open, so by the time the
  59.         // caller calls us again, there's a chance (vanishingly small perhaps)
  60.         // that another app (if our thread were preempted long enough, or the
  61.         // platform is multiprocessor) will have changed the contents of the
  62.         // clipboard to something larger than zero.  Thus, if we copy that
  63.         // into the caller's buffer, the buffer might overflow:
  64.         if (aBuf)
  65.             return 0;
  66.         if (!Open())
  67.         {
  68.             // Since this should be very rare, a shorter message is now used.  Formerly, it was
  69.             // "Could not open clipboard for reading after many timed attempts. Another program is probably holding it open."
  70.             Close(CANT_OPEN_CLIPBOARD_READ);
  71.             return CLIPBOARD_FAILURE;
  72.         }
  73.         if (   !(mClipMemNow = g_clip.GetClipboardDataTimeout(clipboard_contains_files ? CF_HDROP : CF_TEXT))   )
  74.         {
  75.             if (clipboard_contains_files)
  76.             {
  77.                 // v1.0.42.03: For the fix below, GetClipboardDataTimeout() knows not to try more than once
  78.                 // for CF_HDROP.
  79.                 // Fix for v1.0.31.02: When clipboard_contains_files==true, tolerate failure, which happens
  80.                 // as a normal/expected outcome when there are files on the clipboard but either:
  81.                 // 1) zero of them; 2) the CF_HDROP on the clipboard is somehow misformatted.
  82.                 // If you select the parent ".." folder in WinRar then use the following hotkey, the script
  83.                 // would previously yield a runtime error:
  84.                 //#q::
  85.                 //Send, ^c
  86.                 //ClipWait, 0.5, 1
  87.                 //msgbox %Clipboard%
  88.                 //Return
  89.                 Close();
  90.                 if (aBuf)
  91.                     *aBuf = '\0';
  92.                 return 0;
  93.             }
  94.             Close("GetClipboardData"); // Short error message since so rare.
  95.             return CLIPBOARD_FAILURE;
  96.         }
  97.         // Although GlobalSize(mClipMemNow) can yield zero in some cases -- in which case GlobalLock() should
  98.         // not be attempted -- it probably can't yield zero for CF_HDROP and CF_TEXT because such a thing has
  99.         // never been reported by anyone.  Therefore, GlobalSize() is currently not called.
  100.         if (   !(mClipMemNowLocked = (char *)GlobalLock(mClipMemNow))   )
  101.         {
  102.             Close("GlobalLock");  // Short error message since so rare.
  103.             return CLIPBOARD_FAILURE;
  104.         }
  105.         // Otherwise: Update length after every successful new open&lock:
  106.         // Determine the length (size - 1) of the buffer than would be
  107.         // needed to hold what's on the clipboard:
  108.         if (clipboard_contains_files)
  109.         {
  110.             if (file_count = DragQueryFile((HDROP)mClipMemNowLocked, 0xFFFFFFFF, "", 0))
  111.             {
  112.                 mLength = (file_count - 1) * 2;  // Init; -1 if don't want a newline after last file.
  113.                 for (i = 0; i < file_count; ++i)
  114.                     mLength += DragQueryFile((HDROP)mClipMemNowLocked, i, NULL, 0);
  115.             }
  116.             else
  117.                 mLength = 0;
  118.         }
  119.         else // clipboard_contains_text
  120.             mLength = strlen(mClipMemNowLocked);
  121.         if (mLength >= CLIPBOARD_FAILURE) // Can't realistically happen, so just indicate silent failure.
  122.             return CLIPBOARD_FAILURE;
  123.     }
  124.     if (!aBuf)
  125.         return mLength;
  126.         // Above: Just return the length; don't close the clipboard because we expect
  127.         // to be called again soon.  If for some reason we aren't called, MsgSleep()
  128.         // will automatically close the clipboard and clean up things.  It's done this
  129.         // way to avoid the chance that the clipboard contents (and thus its length)
  130.         // will change while we don't have it open, possibly resulting in a buffer
  131.         // overflow.  In addition, this approach performs better because it avoids
  132.         // the overhead of having to close and reopen the clipboard.
  133.  
  134.     // Otherwise:
  135.     if (clipboard_contains_files)
  136.     {
  137.         if (file_count = DragQueryFile((HDROP)mClipMemNowLocked, 0xFFFFFFFF, "", 0))
  138.             for (i = 0; i < file_count; ++i)
  139.             {
  140.                 // Caller has already ensured aBuf is large enough to hold them all:
  141.                 aBuf += DragQueryFile((HDROP)mClipMemNowLocked, i, aBuf, 999);
  142.                 if (i < file_count - 1) // i.e. don't add newline after the last filename.
  143.                 {
  144.                     *aBuf++ = '\r';  // These two are the proper newline sequence that the OS prefers.
  145.                     *aBuf++ = '\n';
  146.                 }
  147.                 //else DragQueryFile() has ensured that aBuf is terminated.
  148.             }
  149.         // else aBuf has already been terminated upon entrance to this function.
  150.     }
  151.     else
  152.         strcpy(aBuf, mClipMemNowLocked);  // Caller has already ensured that aBuf is large enough.
  153.     // Fix for v1.0.37: Close() is no longer called here because it prevents the clipboard variable
  154.     // from being referred to more than once in a line.  For example:
  155.     // Msgbox %Clipboard%%Clipboard%
  156.     // ToolTip % StrLen(Clipboard) . Clipboard
  157.     // Instead, the clipboard is later closed in other places (search on CLOSE_CLIPBOARD_IF_OPEN
  158.     // to find them).  The alternative to fixing it this way would be to let it reopen the clipboard
  159.     // by means getting rid of the following lines above:
  160.     //if (aBuf)
  161.     //    return 0;
  162.     // However, that has the risks described in the comments above those two lines.
  163.     return mLength;
  164. }
  165.  
  166.  
  167.  
  168. ResultType Clipboard::Set(char *aBuf, UINT aLength) //, bool aTrimIt)
  169. // Returns OK or FAIL.
  170. {
  171.     // It was already open for writing from a prior call.  Return failure because callers that do this
  172.     // are probably handling things wrong:
  173.     if (IsReadyForWrite()) return FAIL;
  174.  
  175.     if (!aBuf)
  176.     {
  177.         aBuf = "";
  178.         aLength = 0;
  179.     }
  180.     else
  181.         if (aLength == UINT_MAX) // Caller wants us to determine the length.
  182.             aLength = (UINT)strlen(aBuf);
  183.  
  184.     if (aLength)
  185.     {
  186.         if (!PrepareForWrite(aLength + 1))
  187.             return FAIL;  // It already displayed the error.
  188.         strlcpy(mClipMemNewLocked, aBuf, aLength + 1);  // Copy only a substring, if aLength specifies such.
  189.         // Only trim when the caller told us to, rather than always if g_script.mIsAutoIt2
  190.         // is true, since AutoIt2 doesn't always trim things (e.g. FileReadLine probably
  191.         // does not trim the line that was read into its output var).  UPDATE: This is
  192.         // no longer needed because I think AutoIt2 only auto-trims when SetEnv is used:
  193.         //if (aTrimIt)
  194.         //    trim(mClipMemNewLocked);
  195.     }
  196.     // else just do the below to empty the clipboard, which is different than setting
  197.     // the clipboard equal to the empty string: it's not truly empty then, as reported
  198.     // by IsClipboardFormatAvailable(CF_TEXT) -- and we want to be able to make it truly
  199.     // empty for use with functions such as ClipWait:
  200.     return Commit();  // It will display any errors.
  201. }
  202.  
  203.  
  204.  
  205. char *Clipboard::PrepareForWrite(size_t aAllocSize)
  206. {
  207.     if (!aAllocSize) return NULL; // Caller should ensure that size is at least 1, i.e. room for the zero terminator.
  208.     if (IsReadyForWrite())
  209.         // It was already prepared due to a prior call.  Currently, the most useful thing to do
  210.         // here is return the memory area that's already been reserved:
  211.         return mClipMemNewLocked;
  212.     // Note: I think GMEM_DDESHARE is recommended in addition to the usual GMEM_MOVEABLE:
  213.     // UPDATE: MSDN: "The following values are obsolete, but are provided for compatibility
  214.     // with 16-bit Windows. They are ignored.": GMEM_DDESHARE
  215.     if (   !(mClipMemNew = GlobalAlloc(GMEM_MOVEABLE, aAllocSize))   )
  216.     {
  217.         g_script.ScriptError("GlobalAlloc");  // Short error message since so rare.
  218.         return NULL;
  219.     }
  220.     if (   !(mClipMemNewLocked = (char *)GlobalLock(mClipMemNew))   )
  221.     {
  222.         mClipMemNew = GlobalFree(mClipMemNew);  // This keeps mClipMemNew in sync with its state.
  223.         g_script.ScriptError("GlobalLock"); // Short error message since so rare.
  224.         return NULL;
  225.     }
  226.     mCapacity = (UINT)aAllocSize; // Keep mCapacity in sync with the state of mClipMemNewLocked.
  227.     *mClipMemNewLocked = '\0'; // Init for caller.
  228.     return mClipMemNewLocked;  // The caller can now write to this mem.
  229. }
  230.  
  231.  
  232.  
  233. ResultType Clipboard::Commit(UINT aFormat)
  234. // If this is called while mClipMemNew is NULL, the clipboard will be set to be truly
  235. // empty, which is different from writing an empty string to it.  Note: If the clipboard
  236. // was already physically open, this function will close it as part of the commit (since
  237. // whoever had it open before can't use the prior contents, since they're invalid).
  238. {
  239.     if (!mIsOpen && !Open())
  240.         // Since this should be very rare, a shorter message is now used.  Formerly, it was
  241.         // "Could not open clipboard for writing after many timed attempts.  Another program is probably holding it open."
  242.         return AbortWrite(CANT_OPEN_CLIPBOARD_WRITE);
  243.     if (!EmptyClipboard())
  244.     {
  245.         Close();
  246.         return AbortWrite("EmptyClipboard"); // Short error message since so rare.
  247.     }
  248.     if (mClipMemNew)
  249.     {
  250.         bool new_is_empty = false;
  251.         // Unlock prior to calling SetClipboardData:
  252.         if (mClipMemNewLocked) // probably always true if we're here.
  253.         {
  254.             // Best to access the memory while it's still locked, which is why this temp var is used:
  255.             // v1.0.40.02: The following was fixed to properly recognize 0x0000 as the Unicode string terminator,
  256.             // which fixes problems with Transform Unicode.
  257.             new_is_empty = !mClipMemNewLocked[0] && (aFormat != CF_UNICODETEXT || !mClipMemNewLocked[1]);
  258.             GlobalUnlock(mClipMemNew); // mClipMemNew not mClipMemNewLocked.
  259.             mClipMemNewLocked = NULL;  // Keep this in sync with the above action.
  260.             mCapacity = 0; // Keep mCapacity in sync with the state of mClipMemNewLocked.
  261.         }
  262.         if (new_is_empty)
  263.             // Leave the clipboard truly empty rather than setting it to be the
  264.             // empty string (i.e. these two conditions are NOT the same).
  265.             // But be sure to free the memory since we're not giving custody
  266.             // of it to the system:
  267.             mClipMemNew = GlobalFree(mClipMemNew);
  268.         else
  269.             if (SetClipboardData(aFormat, mClipMemNew))
  270.                 // In any of the failure conditions above, Close() ensures that mClipMemNew is
  271.                 // freed if it was allocated.  But now that we're here, the memory should not be
  272.                 // freed because it is owned by the clipboard (it will free it at the appropriate time).
  273.                 // Thus, we relinquish the memory because we shouldn't be looking at it anymore:
  274.                 mClipMemNew = NULL;
  275.             else
  276.             {
  277.                 Close();
  278.                 return AbortWrite("SetClipboardData"); // Short error message since so rare.
  279.             }
  280.     }
  281.     // else we will close it after having done only the EmptyClipboard(), above.
  282.     // Note: Decided not to update mLength for performance reasons (in case clipboard is huge).
  283.     // Anyway, it seems rather pointless because once the clipboard is closed, our app instantly
  284.     // loses sight of how large it is, so the the value of mLength wouldn't be reliable unless
  285.     // the clipboard were going to be immediately opened again.
  286.     return Close();
  287. }
  288.  
  289.  
  290.  
  291. ResultType Clipboard::AbortWrite(char *aErrorMessage)
  292. // Always returns FAIL.
  293. {
  294.     // Since we were called in conjunction with an aborted attempt to Commit(), always
  295.     // ensure the clipboard is physically closed because even an attempt to Commit()
  296.     // should physically close it:
  297.     Close();
  298.     if (mClipMemNewLocked)
  299.     {
  300.         GlobalUnlock(mClipMemNew); // mClipMemNew not mClipMemNewLocked.
  301.         mClipMemNewLocked = NULL;
  302.         mCapacity = 0; // Keep mCapacity in sync with the state of mClipMemNewLocked.
  303.     }
  304.     // Above: Unlock prior to freeing below.
  305.     if (mClipMemNew)
  306.         mClipMemNew = GlobalFree(mClipMemNew);
  307.     // Caller needs us to always return FAIL:
  308.     return *aErrorMessage ? g_script.ScriptError(aErrorMessage) : FAIL;
  309. }
  310.  
  311.  
  312.  
  313. ResultType Clipboard::Close(char *aErrorMessage)
  314. // Returns OK or FAIL (but it only returns FAIL if caller gave us a non-NULL aErrorMessage).
  315. {
  316.     // Always close it ASAP so that it is free for other apps to use:
  317.     if (mIsOpen)
  318.     {
  319.         if (mClipMemNowLocked)
  320.         {
  321.             GlobalUnlock(mClipMemNow); // mClipMemNow not mClipMemNowLocked.
  322.             mClipMemNowLocked = NULL;  // Keep this in sync with its state, since it's used as an indicator.
  323.         }
  324.         // Above: It's probably best to unlock prior to closing the clipboard.
  325.         CloseClipboard();
  326.         mIsOpen = false;  // Even if above fails (realistically impossible?), seems best to do this.
  327.         // Must do this only after GlobalUnlock():
  328.         mClipMemNow = NULL;
  329.     }
  330.     // Do this cleanup for callers that didn't make it far enough to even open the clipboard.
  331.     // UPDATE: DO *NOT* do this because it is valid to have the clipboard in a "ReadyForWrite"
  332.     // state even after we physically close it.  Some callers rely on that.
  333.     //if (mClipMemNewLocked)
  334.     //{
  335.     //    GlobalUnlock(mClipMemNew); // mClipMemNew not mClipMemNewLocked.
  336.     //    mClipMemNewLocked = NULL;
  337.     //    mCapacity = 0; // Keep mCapacity in sync with the state of mClipMemNewLocked.
  338.     //}
  339.     //// Above: Unlock prior to freeing below.
  340.     //if (mClipMemNew)
  341.     //    // Commit() was never called after a call to PrepareForWrite(), so just free the memory:
  342.     //    mClipMemNew = GlobalFree(mClipMemNew);
  343.     if (aErrorMessage && *aErrorMessage)
  344.         // Caller needs us to always return FAIL if an error was displayed:
  345.         return g_script.ScriptError(aErrorMessage);
  346.  
  347.     // Seems best not to reset mLength.  But it will quickly become out of date once
  348.     // the clipboard has been closed and other apps can use it.
  349.     return OK;
  350. }
  351.  
  352.  
  353.  
  354. HANDLE Clipboard::GetClipboardDataTimeout(UINT uFormat)
  355. // Same as GetClipboardData() except that it doesn't give up if the first call to GetClipboardData() fails.
  356. // Instead, it continues to retry the operation for the number of milliseconds in g_ClipboardTimeout.
  357. // This is necessary because GetClipboardData() has been observed to fail in repeatable situations (this
  358. // is strange because our thread already has the clipboard locked open -- presumably it happens because the
  359. // GetClipboardData() is unable to start a data stream from the application that actually serves up the data).
  360. // If cases where the first call to GetClipboardData() fails, a subsequent call will often succeed if you give
  361. // the owning application (such as Excel and Word) a little time to catch up.  This is especially necessary in
  362. // the OnClipboardChange label, where sometimes a clipboard-change notification comes in before the owning
  363. // app has finished preparing its data for subsequent readers of the clipboard.
  364. {
  365. #ifdef DEBUG_BY_LOGGING_CLIPBOARD_FORMATS  // Provides a convenient log of clipboard formats for analysis.
  366.     static FILE *fp = fopen("c:\\debug_clipboard_formats.txt", "w");
  367. #endif
  368.  
  369.     char format_name[MAX_PATH + 1]; // MSDN's RegisterClipboardFormat() doesn't document any max length, but the ones we're interested in certainly don't exceed MAX_PATH.
  370.     if (uFormat < 0xC000 || uFormat > 0xFFFF) // It's a registered format (you're supposted to verify in-range before calling GetClipboardFormatName()).  Also helps performance.
  371.         *format_name = '\0'; // Don't need the name if it's a standard/CF_* format.
  372.     else
  373.     {
  374.         // v1.0.42.04:
  375.         // Probably need to call GetClipboardFormatName() rather than comparing directly to uFormat because
  376.         // MSDN implies that OwnerLink and other registered formats might not always have the same ID under
  377.         // all OSes (past and future).
  378.         GetClipboardFormatName(uFormat, format_name, MAX_PATH);
  379.         // Since RegisterClipboardFormat() is case insensitive, the case might vary.  So use stricmp() when
  380.         // comparing format_name to anything.
  381.         // "Link Source", "Link Source Descriptor" , and anything else starting with "Link Source" is likely
  382.         // to be data that should not be attempted to be retrieved because:
  383.         // 1) It causes unwanted bookmark effects in various versions of MS Word.
  384.         // 2) Tests show that these formats are on the clipboard only if MS Word is open at the time
  385.         //    ClipboardAll is accessed.  That implies they're transitory formats that aren't as essential
  386.         //    or well suited to ClipboardAll as the other formats (but if it weren't for #1 above, this
  387.         //    wouldn't be enough reason to omit it).
  388.         // 3) Although there is hardly any documentation to be found at MSDN or elsewhere about these formats,
  389.         //    it seems they're related to OLE, with further implications that the data is transitory.
  390.         // Here are the formats that Word 2002 removes from the clipboard when it the app closes:
  391.         // 0xC002 ObjectLink  >>> Causes WORD bookmarking problem.
  392.         // 0xC003 OwnerLink
  393.         // 0xC00D Link Source  >>> Causes WORD bookmarking problem.
  394.         // 0xC00F Link Source Descriptor  >>> Doesn't directly cause bookmarking, but probably goes with above.
  395.         // 0xC0DC Hyperlink
  396.         if (   !strnicmp(format_name, "Link Source", 11) || !stricmp(format_name, "ObjectLink")
  397.             || !stricmp(format_name, "OwnerLink")
  398.             // v1.0.44.07: The following were added to solve interference with MS Outlook's MS Word editor.
  399.             // If a hotkey like ^F1::ClipboardSave:=ClipboardAll is pressed after pressing Ctrl-C in that
  400.             // editor (perhaps only when copying HTML), two of the following error dialogs would otherwise
  401.             // be displayed (this occurs in Outlook 2002 and probably later versions):
  402.             // "An outgoing call cannot be made since the application is dispatching an input-synchronous call."
  403.             || !stricmp(format_name, "Native") || !stricmp(format_name, "Embed Source")   )
  404.             return NULL;
  405.     }
  406.  
  407. #ifdef DEBUG_BY_LOGGING_CLIPBOARD_FORMATS
  408.     fprintf(fp, "%04X\t%s\n", uFormat, format_name);  // Since fclose() is never called, the program has to exit to close/release the file.
  409. #endif
  410.  
  411.     HANDLE h;
  412.     for (DWORD start_time = GetTickCount();;)
  413.     {
  414.         // Known failure conditions:
  415.         // GetClipboardData() apparently fails when the text on the clipboard is greater than a certain size
  416.         // (Even though GetLastError() reports "Operation completed successfully").  The data size at which
  417.         // this occurs is somewhere between 20 to 96 MB (perhaps depending on system's memory and CPU speed).
  418.         if (h = GetClipboardData(uFormat)) // Assign
  419.             return h;
  420.  
  421.         // It failed, so act according to the type of format and the timeout that's in effect.
  422.         // Certain standard (numerically constant) clipboard formats are known to validly yield NULL from a
  423.         // call to GetClipboardData().  Never retry these because it would only cause unnecessary delays
  424.         // (i.e. a failure until timeout).
  425.         // v1.0.42.04: More importantly, retrying them appears to cause problems with saving a Word/Excel
  426.         // clipboard via ClipboardAll.
  427.         if (uFormat == CF_HDROP // This format can fail "normally" for the reasons described at "clipboard_contains_files".
  428.             || !stricmp(format_name, "OwnerLink")) // Known to validly yield NULL from a call to GetClipboardData(), so don't retry it to avoid having to wait the full timeout period.
  429.             return NULL;
  430.  
  431.         if (g_ClipboardTimeout != -1) // We were not told to wait indefinitely and...
  432.             if (!g_ClipboardTimeout   // ...we were told to make only one attempt, or ...
  433.                 || (int)(g_ClipboardTimeout - (GetTickCount() - start_time)) <= SLEEP_INTERVAL_HALF) //...it timed out.
  434.                 // Above must cast to int or any negative result will be lost due to DWORD type.
  435.                 return NULL;
  436.  
  437.         // Use SLEEP_WITHOUT_INTERRUPTION to prevent MainWindowProc() from accepting new hotkeys
  438.         // during our operation, since a new hotkey subroutine might interfere with
  439.         // what we're doing here (e.g. if it tries to use the clipboard, or perhaps overwrites
  440.         // the deref buffer if this object's caller gave it any pointers into that memory area):
  441.         SLEEP_WITHOUT_INTERRUPTION(INTERVAL_UNSPECIFIED)
  442.     }
  443. }
  444.  
  445.  
  446.  
  447. ResultType Clipboard::Open()
  448. {
  449.     if (mIsOpen)
  450.         return OK;
  451.     for (DWORD start_time = GetTickCount();;)
  452.     {
  453.         if (OpenClipboard(g_hWnd))
  454.         {
  455.             mIsOpen = true;
  456.             return OK;
  457.         }
  458.         if (g_ClipboardTimeout != -1) // We were not told to wait indefinitely...
  459.             if (!g_ClipboardTimeout   // ...and we were told to make only one attempt, or ...
  460.                 || (int)(g_ClipboardTimeout - (GetTickCount() - start_time)) <= SLEEP_INTERVAL_HALF) //...it timed out.
  461.                 // Above must cast to int or any negative result will be lost due to DWORD type.
  462.                 return FAIL;
  463.         // Use SLEEP_WITHOUT_INTERRUPTION to prevent MainWindowProc() from accepting new hotkeys
  464.         // during our operation, since a new hotkey subroutine might interfere with
  465.         // what we're doing here (e.g. if it tries to use the clipboard, or perhaps overwrites
  466.         // the deref buffer if this object's caller gave it any pointers into that memory area):
  467.         SLEEP_WITHOUT_INTERRUPTION(INTERVAL_UNSPECIFIED)
  468.     }
  469. }
  470.